/**
 * @file   callbacks.cpp
 * @author Pedro Jimenez
 * @date   Wed Nov  5 14:43:52 2008
 * 
 * @brief  Contiene todos los callback del programa. Callbacks son las funciones que se ejecutan
 * cuando se produce algún evento. Estos callbacks se pueden producir por interacción con el
 * entorno gráfico (pulsar un botón) u otros eventos programados: recibir un dato desde el puerto serie, socket, cerrar el programa, etc.
 * 
 * 
 */
// $Id: callbacks.c,v 1.6 2008-04-04 09:00:12 igdaza Exp $

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#include <gtk/gtk.h>
#include <gdk/gdkx.h>

#include <sys/time.h>

#include "callbacks.h"

#include "interface.h"
#include "support.h"
#include "serial.h"
#include "../pic_code/PICdata.h"

#include "support.h"
#include "other_functions.h"

#include "frame_header.hh"

#include "interface_capture.h"
#include "captura.h"
#include "options.h"

extern captura_params_t captura[MAX_CAMERAS];
extern pthread_t idHiloCapture[2];
extern pthread_t idHiloShow;
extern pthread_t idHiloPrint;
extern socket_params_t * socket_params;

extern GtkWidget *hscale_shutter;
extern GtkWidget *hscale_gain;

/** 
 * Callback de un timeout para finalizar la grabación pasado el tiempo expecificado en la llamada a esta función (aprox 1h 45')
 * 
 * @param params No se le pasa ningún parámetro
 * 
 * @return False para mater el temporizador, solo se ejecuta una vez
 */
gboolean timeout_finalizar_grabacion(gpointer params)
{
    GtkWidget *button_stop = lookup_widget(window1, "button_stop");
    printf("timeout_finalizar_grabacion\n"); fflush(stdout);
    gtk_signal_emit_by_name(GTK_OBJECT(button_stop), "clicked");
    return false;

}
/**
 * Callback de un timeout para iniciar la grabación pasado un segundo. De ese modo hay tiempo par inicializar las cámaras y todo lo necesario antes
 * 
 * @param params No se le pasa ningún parámetro
 * 
 * @return False para mater el temporizador, solo re ejecuta una vez
 */
gboolean timeout_iniciar_grabacion(gpointer params)
{
    bool * pgrabar_flag = (bool*)params;
    *pgrabar_flag = true;
    printf ("\nInicio de la grabación.\n\nFile Name: %s\n\n",captura[0].filename);
    //devolvemos false para matar el timer
    return false;
}

void on_exit_clicked (GtkButton *button, gpointer user_data)
{
    

}

/** 
 * Callback que se ejecuta al pulsar el botón de inicio de grabación.
 * 
 * @param button 
 * @param user_data Debe apuntar a  checkbutton_external_sync, el botón de sincronismo externo. Esta función cambia el checked de dicho botón
 */void on_grabar_clicked (GtkButton *button, gpointer user_data)
{
    char cabecera[1000];
    char filename[2][1000];
    int len1, len0, i;
    
    if (esta_grabando)
	return;
    
    esta_grabando = true;

    cvDestroyWindow("Enfoque");
    //onmouse(7, 1,1, 0, NULL);
    hold_window = false;

    
    //lee cual es el siguiente video que toca, y añade fecha y hora.
    get_name_file_by_date(cabecera, ini_opts("nextfile"), (ini_opt("next")==0?0: -1));
    for (i=0; i<captura[0].camera[0].numCams; i++)
    {
	strcpy(filename[i], ini_opts("videopath", i));
	if (filename[i][strlen(filename[i])-1] != '/')
	    strcat(filename[i], "/");
	strcat(filename[i], ini_opts("videoname"));
	strcat(filename[i], cabecera);
	
	if (ini_opt("raw"))
	    strcat(filename[i], ".raw");
	else 
	    strcat(filename[i], ".avi");
    }

    gtk_label_set_markup(GTK_LABEL(label_file_name),filename[0]);

    GtkWidget *button2 = lookup_widget(window1, "button_record");
    gtk_widget_set_sensitive(button2, false);
    button2 = lookup_widget(window1, "button_stop");
    gtk_widget_set_sensitive(button2, true);

    // Se llama a la función "on_checkbutton_sync_external_activate"
    if (ini_opt("extsync"))
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (user_data), true);
    //    on_checkbutton_sync_external_activate(user_data, NULL);
 
    if (captura[0].camera[0].numCams == 2)
    {
	len0 = strlen(filename[0]);
	len1 = strlen(filename[1]);
	strcpy(filename[0] + len0-4, "_1");
	strcat(filename[0], filename[1] + len1-4);
	strcpy(filename[1] + len1-4, filename[0]+len0-4);
	filename[1][len1-3] = '2';
    }

    for (i=0; i<captura[0].camera[0].numCams; i++)
    {
	if (ini_opt("raw"))
	    iniciar_grabacion(&captura[i], filename[i]);
	else 
	    iniciar_compresion(&captura[i], filename[i]);
    }

    ///iniciar la grabación pasado 1 segundo, para dejar tiempo a que se vacíe el buffer
    iniciado_grabar = false;
    g_timeout_add(1000, timeout_iniciar_grabacion, (void*)&grabar_flag);
    /// Finaliza la grabación pasados 1h 45', tiempo máximo que se espera que dura una prueba 
    g_timeout_add((45+60)*60*1000, timeout_finalizar_grabacion, (void*)&grabar_flag);
}


/** 
 *  callback que se ejecuta cuando se pulsa el Botón de parar la grabación
 * 
 * @param user_data puntero a (gpointer) checkbutton_external_sync, que el objeto del checkbox de sincronismo externo
 */
void on_stop_clicked (GtkButton *button, gpointer user_data)
{
    if (grabar_flag)
    {
	GtkWidget *button = lookup_widget(window1, "button_record");
	gtk_widget_set_sensitive(button, true);
	button = lookup_widget(window1, "button_stop");
	gtk_widget_set_sensitive(button, false);

	iniciado_grabar = false;
	printf ("\nFin de la grabación.\n");
	// Se llama a la función "on_checkbutton_sync_external_activate"
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (user_data), false);
	//    on_checkbutton_sync_external_activate(user_data, NULL);
	grabar_flag = false;
	esta_grabando = false;
	
	finalizar_grabacion(&captura[0]);
	if (captura[0].camera[0].numCams >=2)
	    finalizar_grabacion(&captura[1]);
    }
}

/** 
 * Callback de la barra de desplazamiento para cambiar la ganancia
 * 
 * @param range 
 * @param user_data 
 */
void on_hscale_gain (GtkRange *range, gpointer user_data)
{
    GtkAdjustment* gain;

    gain = gtk_range_get_adjustment(range);
    gain_value = gtk_adjustment_get_value(gain);
    for (int i=0; i<captura[0].camera[0].numCams; i++)
	dc_set_feature(captura[i].camera, 0, FEATURE_GAIN, (int)gain_value);
}

/** 
 * Callback de la barra de desplazamiento para cambiar el brillo
 * 
 * @param range 
 * @param user_data 
 */
void on_hscale_brightness (GtkRange *range, gpointer user_data)
{
    GtkAdjustment* brightness;
    //gdouble brightness_value;

    brightness = gtk_range_get_adjustment(range);
    brightness_value = gtk_adjustment_get_value(brightness);
    for (int i=0; i<captura[0].camera[0].numCams; i++)
	dc_set_feature(captura[i].camera, 0, FEATURE_BRIGHTNESS, (int)brightness_value);
}

/** 
 * Callback de la barra de desplazamiento para cambiar el tiempo de exposición
 * Conveierte el nuevo valor de exposición a unidades de la cámara y manda el dato por el firewire, a las dos cámaras
 * @param range 
 * @param user_data 
 */
void on_hscale_shutter (GtkRange *range, gpointer user_data)
{
    GtkAdjustment* shutter;
    gdouble shutter_value_scale;
    //gdouble shutter_value; // Valor a transmitir en milisegundos
    unsigned char reg_tx;

    shutter = gtk_range_get_adjustment(range);
    captura[0].camera->shutter_value = gtk_adjustment_get_value(shutter);
    captura[1].camera->shutter_value = captura[0].camera->shutter_value;
    for (int i=0; i<captura[0].camera[0].numCams; i++)
	dc_set_feature(captura[i].camera, 0, FEATURE_SHUTTER, (int)captura[i].camera->shutter_value);
    shutter_value_scale = captura[0].camera->shutter_value * 0.02;

    if (shutter_value_scale >= 5.0 && shutter_value_scale <= 32.0)
    {
	if (is_open_port(p_Serial_Params))
	{
	    reg_tx = (unsigned char)shutter_value_scale;
	    reg_tx = reg_tx & FORMAT_DATA;
	    reg_tx = PICCAB_SHUTTER_TIME | reg_tx;
	    Write_Port_buffer(p_Serial_Params, &reg_tx , 1, false);
	}
    }
    
}
/** 
 * Callback de la barra de desplazamiento para cambiar la iluminación activa IR
 * Si hay una placa de sincronismo conectada al puerto serie, manda el nuevo dato de iluminación
 * a través del puerto serie a la placa
 * @param range 
 * @param user_data 
 */
void on_hscale_light_intensity (GtkRange *range, gpointer user_data)
{
    GtkAdjustment* light_intensity;
    //int light_intensity_value;
    //int  i=0;

    // Se lee la información de la barra de estado
    light_intensity = gtk_range_get_adjustment(range);
    light_intensity_value = (int)gtk_adjustment_get_value(light_intensity);

    // Se transmite la información leida
    tx_dato_intensidad (light_intensity_value/2);
}

/** 
 * Envia al PIC el ciclo de trabajo del PWM al que se quiere que trabajen los
 * diodos de iluminación infrarroja.
 * 
 * @param dato_tx Iluminación deseada. Un valor de 0 (apagado) a 63
 */void tx_dato_intensidad (int dato_tx)
{
    unsigned char reg_tx;

    if (is_open_port(p_Serial_Params))
    {
	reg_tx = (unsigned char)dato_tx;
	reg_tx = reg_tx & FORMAT_DATA;
	reg_tx = PICCAB_PWM_VALUE | reg_tx;
 
	printf("Duty_Cicle: %d\n",reg_tx);
	Write_Port_buffer(p_Serial_Params, &reg_tx , 1, false);
    }
}

/** 
 * Callback al pulsar sobre la casilla de activación de sincronismo externo
 * Activa o desactiva el sincronismo externo (sincronismo con la placa de sincro),
 * según corresponda, enviando los datos necesarios
 * a la placa de sincro a través del puerto serie y a las cámaras a través del firewire
 *  Si se activa el sincronismo externo, esta función también activa el sincronismo de las cámaras.
 * @param user_data puntero a (gpointer) checkbutton_cam_sync, para modificar las opciones de 
 * sincronismo de la cámara
 */void on_checkbutton_sync_external_activate  (GtkButton *button,gpointer user_data)
{
    unsigned char reg_tx = 0x00; 

    if (GTK_TOGGLE_BUTTON (button)->active) 
    {
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (user_data), false);

	reg_tx = PICCAB_MODE | PICCAB_EXT_SYNC_ON;
	printf("\n********** Sincronismo con señal externa ***************\n");
	external_sync[0] = SYNC_EXTERNAL_ON;
	external_sync[1] = SYNC_EXTERNAL_ON;
	external_sync_mode_on = true;
    }
    else 
    {
	reg_tx = PICCAB_MODE | PICCAB_EXT_SYNC_OFF;
	printf("\n********** Sin sincronismo  ***************\n");
	external_sync[0] = SYNC_EXTERNAL_OFF;
	external_sync[1] = SYNC_EXTERNAL_OFF;
	external_sync_mode_on = false;
    }

    if (is_open_port(p_Serial_Params))
	Write_Port_buffer(p_Serial_Params, &reg_tx , 1, true);
}

/** 
 * Callback al pulsar sobre la casilla de activación de sincronismo interno: 
 * sincronismo de una cámara con la otra, pero sin placa de sincro.
 * Activa a desactiva el sincronismo interno, según corresponda, enviando los datos necesarios
 * a la placa de sincro a través del puerto serie y a las cámaras a través del firewire
 * 
 * @param user_data nada
 */void on_checkbutton_sync_camera_activate (GtkButton  *button,gpointer user_data)
{
    unsigned char reg_tx = 0x00; 

    if (GTK_TOGGLE_BUTTON (button)->active) 
    {
	external_sync[0] = SYNC_EXTERNAL_ON;
	external_sync[1] = SYNC_EXTERNAL_ON;
	reg_tx = PICCAB_MODE | PICCAB_CAM_SYNC_ON;
	printf("\n********** Sincronismo interno  ***************\n");
    }
    else 
    {
	external_sync[0] = SYNC_EXTERNAL_OFF;
	external_sync[1] = SYNC_EXTERNAL_OFF;
	reg_tx = PICCAB_MODE | PICCAB_CAM_SYNC_OFF;
	printf("\n********** Sin Sincronismo***************\n");
    }

    if (is_open_port(p_Serial_Params))
	Write_Port_buffer(p_Serial_Params, &reg_tx , 1, true);
}


/** 
 * Callback al pulsar el boton de simulación de sincronismo
 * La placa de sincro normalmente recibe la señal de sincronismo externo a través del cable de sincro.
 * Se le puede indicar a la placa que genere el sincronismo aunque el cable de sincro esté desconectado. 
 * Esta función envía los comandos necesarios a la placa por el puerto serie para activar a desactivar esta función.
 * 
 */void on_checkbutton_sim_sync (GtkButton *button,gpointer user_data)
{
    unsigned char reg_tx = 0x00; 

    if (GTK_TOGGLE_BUTTON (button)->active) 
    {
	reg_tx = PICCAB_MODE | PICCAB_EXT_SYNC_SIM_ON;
	printf("\n********** SIMULANDO el Sincronismo externo  ***************\n");
    }
    else 
    {
	reg_tx = PICCAB_MODE | PICCAB_EXT_SYNC_SIM_OFF;
	printf("\n********** NO SIMULADO Sincronismo ***************\n");
    }

    if (is_open_port(p_Serial_Params))
	Write_Port_buffer(p_Serial_Params, &reg_tx , 1, true);
}



/** 
 * Se ejecuta cuando se recibe un nuevo dato desde le puerto serie.
 * Para cada dato enviado se debe recibir un dato por el puerto. El valor de dicho byte recibido indica el estado de funcionamiento de la placa de sincronismo
 * 
 * @param datorx_in Último dato que se ha recibido por el puerto serie
 * @param datotx Último dato que se había enviado por el puerto, para el cual todavía no hay respuesta
 * @param time_lapso Tiempo transcurrido desde que se envio datotx hasta que se recibió datarx_in
 * @param p estructura de datos con los parámetros del puerto serie, etc.
 * @param params nada 
 * 
 * @return 
 */bool datos_recibido(int datorx_in, unsigned char datotx, float time_lapso, serial_params_t *p, void *params)
{
    unsigned char datorx;

    // si no se ha recibido algún dato, se lanza la función con datorx = -1;
    if (datorx_in == -1)
    {
	// el dato no se recibió, lo quita de la cola
	p->buffer_ack.pop();
	printf("No se recibió el ACK del dato 0x%X tras %6.0f ms.\n", datotx, time_lapso);
	if (p->datos_recibidos == 0)
	{
	    printf("\nERROR *********************************");
	    printf("Error del puerto Serie: No se ha recibido ningún dato:\n");
	    printf("\n\n\tEsta conectado el puerto serie?\n\n");
	    return false;
	}
	return true;
    }
    
    p->datos_recibidos=1;

    datorx = (unsigned char)datorx_in;

    if ((datorx & FORMAT_CAB) == PICCAB_MODE)
    {  //se enviÃ³ un dato de peticiÃ³n de sincronismo
	if (!(datorx & 0x1))
	{
	    /*
	    if (grabar_flag && iniciado_grabar)
	    {
		GtkWidget *window1 = (GtkWidget *)params;
		if (window1 != NULL)
		{
		    GtkWidget *button_stop = lookup_widget(window1, "button_stop");
		    gtk_signal_emit_by_name(GTK_OBJECT(button_stop), "clicked");
		}
		
	    }
	    */
	    if (external_sync_mode_on == true)
	    {
		printf ("Sync signal OFF\n");
	    }
	}
#ifdef DEBUG_SHOW_SERIALPORT_DATA
	else
	    printf("Sync signal ON\n");
#endif


	// sepone el Ãºltimo bit a '0' para que no de error la comprobaciÃ³n del ack
	datotx &= 0xFE;
	datorx &= 0xFE;
    }

    //comprueba que el dato recibido es el correcto. el PIC reenvÃ­a el dato recibido
    if (datotx != datorx)
	printf("0x%X Error de Ack. received 0x%X\n", datotx, datorx);
#ifdef DEBUG_SHOW_SERIALPORT_DATA
    else
	printf("0x%X ack OK, after %6.0f ms\n", datotx, time_lapso);
#endif

    fflush(stdout);
    //true porque se quiere que siga activo el hilo de recepciÃ³n
    return true;
}


/** 
 * Callback que se ejecuta cuando se recibe algún dato desde otro equipo a través del socket
 * 
 * @param socket_params estructura de datos de los socket
 * @param params puntero al objeto de la ventana principal del entorno gráfico
 * 
 * @return false para cortar la comunicación entre hosts.
 */bool socket_callback(socket_params_t * socket_params, void* params)
{
    GtkWidget *window1 = (GtkWidget *)params;
    GtkWidget *button;

    //printf("socket: %d %d\n", grabar_flag, socket_params->master_is_grab);
    if (/*grabar_flag == false &&*/ socket_params->master_is_grab == true)
    {
	printf("\nserver inició la grabación\n");

	gdk_threads_enter();
	button = lookup_widget(window1, "button_stop");
	gtk_signal_emit_by_name(GTK_OBJECT(button), "clicked");

	sched_yield();
	button = lookup_widget(window1, "button_record");
	gtk_signal_emit_by_name(GTK_OBJECT(button), "clicked");
	gdk_threads_leave();
    }
    else if (grabar_flag == true && socket_params->master_is_grab == false)
    {
	printf("\nserver paró la grabación\n");
	button = lookup_widget(window1, "button_stop");
	gtk_signal_emit_by_name(GTK_OBJECT(button), "clicked");
    }

    return true;
}

/** 
 * Callback que se genera al cerrar el programa
 * Se encarga de acabar la grabación en caso necesario, y terminar todos los hilos activos
 * 
 * @param object 
 * @param user_data 
 */
void on_salir_activate (GtkObject *object, gpointer user_data)
{
    //cvDestroyWindow(SHOW_WINDOW[0]);
    //cvDestroyWindow(SHOW_WINDOW[1]);
    //cvDestroyWindow("Enfoque");
    pthread_cancel(idHiloPrint);
    //pthread_cancel(idHiloShow);
    finish_captura_and_save(captura);
    if (idHiloCapture[0])
	pthread_cancel(idHiloCapture[0]);
    if (idHiloCapture[1])
	pthread_cancel(idHiloCapture[1]);
    sched_yield();
    pthread_join(idHiloPrint, NULL);
    if (idHiloCapture[0])
	pthread_join(idHiloCapture[0], NULL);
    if (idHiloCapture[1])
	pthread_join(idHiloCapture[1], NULL);
    //pthread_join(idHiloShow, NULL);

    printf("\nFin hilo Capture y show\n");
    terminar_lectura_serialport(p_Serial_Params);
    if (socket_params)
	socket_params = finish_slave_socket(socket_params);
    if (socket_params)
	socket_params = finish_master_socket(socket_params);
    //system("kill -9 `pidof gmplayer`");
    gtk_exit(0);
}


//@}
